/*
 * Javascript EXIF Reader - jQuery plugin 0.1.3
 * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com, http://blog.nihilogic.dk/
 * Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
 */
function BinaryFile() {
	this.data = null;
	this.dataLength = 0;
	this.callback = null;
	this.id = -1;
}

BinaryFile.prototype.readFile = function(jpegFile, id, callback) {
	this.id = id;
	this.callback = callback;	
	this.fileReader = new FileReader();
	this.fileReader.bf = this;
	this.fileReader.onload = function(e) {
		this.bf.processData(this.result);
	};
	this.fileReader.readAsBinaryString(jpegFile);
}

BinaryFile.prototype.processData = function(result) {
	this.data = result;
	this.dataLength = this.data.length;
	this.dataOffset = 0;
	this.fileReader.bf = null;
	this.fileReader = null;
	if(this.callback) {
		this.callback(this);
	}
}

BinaryFile.prototype.getByteAt = function(iOffset) {
	return this.data.charCodeAt(iOffset + this.dataOffset) & 0xFF;
}

BinaryFile.prototype.getIEByteAt = function(iOffset) {
	return IEBinary_getByteAt(this.data, iOffset + this.dataOffset);
}

BinaryFile.prototype.getLength = function() {
	return this.dataLength;
}

BinaryFile.prototype.getSByteAt = function(iOffset) {
	var iByte = this.getByteAt(iOffset);
	if(iByte > 127)
		return iByte - 256;
	else
		return iByte;
}

BinaryFile.prototype.getShortAt = function(iOffset, bBigEndian) {
	var iShort = bBigEndian ? (this.getByteAt(iOffset) << 8) + this.getByteAt(iOffset + 1) : (this.getByteAt(iOffset + 1) << 8) + this.getByteAt(iOffset)
	if(iShort < 0)
		iShort += 65536;
	return iShort;
}

BinaryFile.prototype.getSShortAt = function(iOffset, bBigEndian) {
	var iUShort = this.getShortAt(iOffset, bBigEndian);
	if(iUShort > 32767)
		return iUShort - 65536;
	else
		return iUShort;
}

BinaryFile.prototype.getLongAt = function(iOffset, bBigEndian) {
	var iByte1 = this.getByteAt(iOffset), iByte2 = this.getByteAt(iOffset + 1), iByte3 = this.getByteAt(iOffset + 2), iByte4 = this.getByteAt(iOffset + 3);

	var iLong = bBigEndian ? (((((iByte1 << 8) + iByte2) << 8) + iByte3) << 8) + iByte4 : (((((iByte4 << 8) + iByte3) << 8) + iByte2) << 8) + iByte1;
	if(iLong < 0)
		iLong += 4294967296;
	return iLong;
}

BinaryFile.prototype.getSLongAt = function(iOffset, bBigEndian) {
	var iULong = this.getLongAt(iOffset, bBigEndian);
	if(iULong > 2147483647)
		return iULong - 4294967296;
	else
		return iULong;
}

BinaryFile.prototype.getStringAt = function(iOffset, iLength) {
	var aStr = [];
	for(var i = iOffset, j = 0; i < iOffset + iLength; i++, j++) {
		aStr[j] = String.fromCharCode(this.getByteAt(i));
	}
	return aStr.join("");
}

BinaryFile.prototype.getCharAt = function(iOffset) {
	return String.fromCharCode(this.getByteAt(iOffset));
}

BinaryFile.prototype.toBase64 = function() {
	return window.btoa(this.data);
}

BinaryFile.prototype.fromBase64 = function(strBase64) {
	this.data = window.atob(strBase64);
}
//
////////////////////////////////////////////////////////////////////
//  EXIF Definition starts here.
////////////////////////////////////////////////////////////////////
// function EXIF() {
	// this.bDebug = false;
// 
// }

var EXIF = {};

EXIF.imageHasData = function(oImg) {
	return !!(oImg.exifdata);
}

EXIF.findEXIFinJPEG = function(oFile) {
	var aMarkers = [];

	if(oFile.getByteAt(0) != 0xFF || oFile.getByteAt(1) != 0xD8) {
		return false;
		// not a valid jpeg
	}

	var iOffset = 2;
	var iLength = oFile.getLength();
	while(iOffset < iLength) {
		if(oFile.getByteAt(iOffset) != 0xFF) {
			if(bDebug)
				console.log("Not a valid marker at offset " + iOffset + ", found: " + oFile.getByteAt(iOffset));
			return false;
			// not a valid marker, something is wrong
		}

		var iMarker = oFile.getByteAt(iOffset + 1);

		// we could implement handling for other markers here,
		// but we're only looking for 0xFFE1 for EXIF data

		if(iMarker == 22400) {
			if(this.bDebug)
				console.log("Found 0xFFE1 marker");
			return readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset + 2, true) - 2);
			iOffset += 2 + oFile.getShortAt(iOffset + 2, true);

		} else if(iMarker == 225) {
			// 0xE1 = Application-specific 1 (for EXIF)
			if(this.bDebug)
				console.log("Found 0xFFE1 marker");
			return this.readEXIFData(oFile, iOffset + 4, oFile.getShortAt(iOffset + 2, true) - 2);

		} else {
			iOffset += 2 + oFile.getShortAt(iOffset + 2, true);
		}
	}
}

EXIF.readTags = function(oFile, iTIFFStart, iDirStart, oStrings, bBigEnd) {
	var iEntries = oFile.getShortAt(iDirStart, bBigEnd);
	var oTags = {};
	for(var i = 0; i < iEntries; i++) {
		var iEntryOffset = iDirStart + i * 12 + 2;
		var strTag = oStrings[oFile.getShortAt(iEntryOffset, bBigEnd)];
		if(!strTag && this.bDebug)
			console.log("Unknown tag: " + oFile.getShortAt(iEntryOffset, bBigEnd));
		oTags[strTag] = this.readTagValue(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd);
	}
	return oTags;
}

EXIF.readTagValue = function(oFile, iEntryOffset, iTIFFStart, iDirStart, bBigEnd) {
	var iType = oFile.getShortAt(iEntryOffset + 2, bBigEnd);
	var iNumValues = oFile.getLongAt(iEntryOffset + 4, bBigEnd);
	var iValueOffset = oFile.getLongAt(iEntryOffset + 8, bBigEnd) + iTIFFStart;

	switch (iType) {
		case 1:
		// byte, 8-bit unsigned int
		case 7:
			// undefined, 8-bit byte, value depending on field
			if(iNumValues == 1) {
				return oFile.getByteAt(iEntryOffset + 8, bBigEnd);
			} else {
				var iValOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8);
				var aVals = [];
				for(var n = 0; n < iNumValues; n++) {
					aVals[n] = oFile.getByteAt(iValOffset + n);
				}
				return aVals;
			}
			break;

		case 2:
			// ascii, 8-bit byte
			var iStringOffset = iNumValues > 4 ? iValueOffset : (iEntryOffset + 8);
			return oFile.getStringAt(iStringOffset, iNumValues - 1);
			break;

		case 3:
			// short, 16 bit int
			if(iNumValues == 1) {
				return oFile.getShortAt(iEntryOffset + 8, bBigEnd);
			} else {
				var iValOffset = iNumValues > 2 ? iValueOffset : (iEntryOffset + 8);
				var aVals = [];
				for(var n = 0; n < iNumValues; n++) {
					aVals[n] = oFile.getShortAt(iValOffset + 2 * n, bBigEnd);
				}
				return aVals;
			}
			break;

		case 4:
			// long, 32 bit int
			if(iNumValues == 1) {
				return oFile.getLongAt(iEntryOffset + 8, bBigEnd);
			} else {
				var aVals = [];
				for(var n = 0; n < iNumValues; n++) {
					aVals[n] = oFile.getLongAt(iValueOffset + 4 * n, bBigEnd);
				}
				return aVals;
			}
			break;
		case 5:
			// rational = two long values, first is numerator, second is denominator
			if(iNumValues == 1) {
				return oFile.getLongAt(iValueOffset, bBigEnd) / oFile.getLongAt(iValueOffset + 4, bBigEnd);
			} else {
				var aVals = [];
				for(var n = 0; n < iNumValues; n++) {
					aVals[n] = oFile.getLongAt(iValueOffset + 8 * n, bBigEnd) / oFile.getLongAt(iValueOffset + 4 + 8 * n, bBigEnd);
				}
				return aVals;
			}
			break;
		case 9:
			// slong, 32 bit signed int
			if(iNumValues == 1) {
				return oFile.getSLongAt(iEntryOffset + 8, bBigEnd);
			} else {
				var aVals = [];
				for(var n = 0; n < iNumValues; n++) {
					aVals[n] = oFile.getSLongAt(iValueOffset + 4 * n, bBigEnd);
				}
				return aVals;
			}
			break;
		case 10:
			// signed rational, two slongs, first is numerator, second is denominator
			if(iNumValues == 1) {
				return oFile.getSLongAt(iValueOffset, bBigEnd) / oFile.getSLongAt(iValueOffset + 4, bBigEnd);
			} else {
				var aVals = [];
				for(var n = 0; n < iNumValues; n++) {
					aVals[n] = oFile.getSLongAt(iValueOffset + 8 * n, bBigEnd) / oFile.getSLongAt(iValueOffset + 4 + 8 * n, bBigEnd);
				}
				return aVals;
			}
			break;
	}
}

EXIF.readEXIFData = function(oFile, iStart, iLength) {
	if(oFile.getStringAt(iStart, 4) != "Exif") {
		if(this.bDebug)
			console.log("Not valid EXIF data! " + oFile.getStringAt(iStart, 4));
		return false;
	}

	var bBigEnd;

	var iTIFFOffset = iStart + 6;

	// test for TIFF validity and endianness
	if(oFile.getShortAt(iTIFFOffset) == 0x4949) {
		bBigEnd = false;
	} else if(oFile.getShortAt(iTIFFOffset) == 0x4D4D) {
		bBigEnd = true;
	} else {
		if(bDebug)
			console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
		return false;
	}

	if(oFile.getShortAt(iTIFFOffset + 2, bBigEnd) != 0x002A) {
		if(this.bDebug)
			console.log("Not valid TIFF data! (no 0x002A)");
		return false;
	}

	if(oFile.getLongAt(iTIFFOffset + 4, bBigEnd) != 0x00000008) {
		if(this.bDebug)
			console.log("Not valid TIFF data! (First offset not 8)", oFile.getShortAt(iTIFFOffset + 4, bBigEnd));
		return false;
	}

	var oTags = this.readTags(oFile, iTIFFOffset, iTIFFOffset + 8, EXIF.TiffTags, bBigEnd);

	if(oTags.ExifIFDPointer) {
		var oEXIFTags = this.readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.ExifIFDPointer, EXIF.Tags, bBigEnd);
		for(var strTag in oEXIFTags) {
			switch (strTag) {
				case "LightSource" :
				case "Flash" :
				case "MeteringMode" :
				case "ExposureProgram" :
				case "SensingMethod" :
				case "SceneCaptureType" :
				case "SceneType" :
				case "CustomRendered" :
				case "WhiteBalance" :
				case "GainControl" :
				case "Contrast" :
				case "Saturation" :
				case "Sharpness" :
				case "SubjectDistanceRange" :
				case "FileSource" :
					oEXIFTags[strTag] = EXIF.StringValues[strTag][oEXIFTags[strTag]];
					break;

				case "ExifVersion" :
				case "FlashpixVersion" :
					oEXIFTags[strTag] = String.fromCharCode(oEXIFTags[strTag][0], oEXIFTags[strTag][1], oEXIFTags[strTag][2], oEXIFTags[strTag][3]);
					break;

				case "ComponentsConfiguration" :
					oEXIFTags[strTag] = EXIF.StringValues.Components[oEXIFTags[strTag][0]] + EXIF.StringValues.Components[oEXIFTags[strTag][1]] + EXIF.StringValues.Components[oEXIFTags[strTag][2]] + EXIF.StringValues.Components[oEXIFTags[strTag][3]];
					break;
			}
			oTags[strTag] = oEXIFTags[strTag];
		}
	}

	if(oTags.GPSInfoIFDPointer) {
		var oGPSTags = this.readTags(oFile, iTIFFOffset, iTIFFOffset + oTags.GPSInfoIFDPointer, EXIF.GPSTags, bBigEnd);
		for(var strTag in oGPSTags) {
			switch (strTag) {
				case "GPSVersionID" :
					oGPSTags[strTag] = oGPSTags[strTag][0] + "." + oGPSTags[strTag][1] + "." + oGPSTags[strTag][2] + "." + oGPSTags[strTag][3];
					break;
			}
			oTags[strTag] = oGPSTags[strTag];
		}
	}

	return oTags;
}

EXIF.getData = function(oImg, fncCallback) {
	if(!oImg.complete)
		return false;
	if(!imageHasData(oImg)) {
		getImageData(oImg, fncCallback);
	} else {
		if(fncCallback)
			fncCallback();
	}
	return true;
}

EXIF.getTag = function(oImg, strTag) {
	if(!imageHasData(oImg))
		return;
	return oImg.exifdata[strTag];
}

EXIF.getAllTags = function(oImg) {
	if(!imageHasData(oImg))
		return {};
	var oData = oImg.exifdata;
	var oAllTags = {};
	for(var a in oData) {
		if(oData.hasOwnProperty(a)) {
			oAllTags[a] = oData[a];
		}
	}
	return oAllTags;
}

EXIF.pretty = function(oImg) {
	if(!imageHasData(oImg))
		return "";
	var oData = oImg.exifdata;
	var strPretty = "";
	for(var a in oData) {
		if(oData.hasOwnProperty(a)) {
			if( typeof oData[a] == "object") {
				strPretty += a + " : [" + oData[a].length + " values]\r\n";
			} else {
				strPretty += a + " : " + oData[a] + "\r\n";
			}
		}
	}
	return strPretty;
}

EXIF.readFromBinaryFile = function(oFile) {
	return findEXIFinJPEG(oFile);
}

EXIF.Tags = {

	// version tags
	0x9000 : "ExifVersion", // EXIF version
	0xA000 : "FlashpixVersion", // Flashpix format version

	// colorspace tags
	0xA001 : "ColorSpace", // Color space information tag

	// image configuration
	0xA002 : "PixelXDimension", // Valid width of meaningful image
	0xA003 : "PixelYDimension", // Valid height of meaningful image
	0x9101 : "ComponentsConfiguration", // Information about channels
	0x9102 : "CompressedBitsPerPixel", // Compressed bits per pixel

	// user information
	0x927C : "MakerNote", // Any desired information written by the manufacturer
	0x9286 : "UserComment", // Comments by user

	// related file
	0xA004 : "RelatedSoundFile", // Name of related sound file

	// date and time
	0x9003 : "DateTimeOriginal", // Date and time when the original image was generated
	0x9004 : "DateTimeDigitized", // Date and time when the image was stored digitally
	0x9290 : "SubsecTime", // Fractions of seconds for DateTime
	0x9291 : "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
	0x9292 : "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized

	// picture-taking conditions
	0x829A : "ExposureTime", // Exposure time (in seconds)
	0x829D : "FNumber", // F number
	0x8822 : "ExposureProgram", // Exposure program
	0x8824 : "SpectralSensitivity", // Spectral sensitivity
	0x8827 : "ISOSpeedRatings", // ISO speed rating
	0x8828 : "OECF", // Optoelectric conversion factor
	0x9201 : "ShutterSpeedValue", // Shutter speed
	0x9202 : "ApertureValue", // Lens aperture
	0x9203 : "BrightnessValue", // Value of brightness
	0x9204 : "ExposureBias", // Exposure bias
	0x9205 : "MaxApertureValue", // Smallest F number of lens
	0x9206 : "SubjectDistance", // Distance to subject in meters
	0x9207 : "MeteringMode", // Metering mode
	0x9208 : "LightSource", // Kind of light source
	0x9209 : "Flash", // Flash status
	0x9214 : "SubjectArea", // Location and area of main subject
	0x920A : "FocalLength", // Focal length of the lens in mm
	0xA20B : "FlashEnergy", // Strobe energy in BCPS
	0xA20C : "SpatialFrequencyResponse", //
	0xA20E : "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
	0xA20F : "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
	0xA210 : "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
	0xA214 : "SubjectLocation", // Location of subject in image
	0xA215 : "ExposureIndex", // Exposure index selected on camera
	0xA217 : "SensingMethod", // Image sensor type
	0xA300 : "FileSource", // Image source (3 == DSC)
	0xA301 : "SceneType", // Scene type (1 == directly photographed)
	0xA302 : "CFAPattern", // Color filter array geometric pattern
	0xA401 : "CustomRendered", // Special processing
	0xA402 : "ExposureMode", // Exposure mode
	0xA403 : "WhiteBalance", // 1 = auto white balance, 2 = manual
	0xA404 : "DigitalZoomRation", // Digital zoom ratio
	0xA405 : "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
	0xA406 : "SceneCaptureType", // Type of scene
	0xA407 : "GainControl", // Degree of overall image gain adjustment
	0xA408 : "Contrast", // Direction of contrast processing applied by camera
	0xA409 : "Saturation", // Direction of saturation processing applied by camera
	0xA40A : "Sharpness", // Direction of sharpness processing applied by camera
	0xA40B : "DeviceSettingDescription", //
	0xA40C : "SubjectDistanceRange", // Distance to subject

	// other tags
	0xA005 : "InteroperabilityIFDPointer",
	0xA420 : "ImageUniqueID"		// Identifier assigned uniquely to each image
};

EXIF.TiffTags = {
	0x0100 : "ImageWidth",
	0x0101 : "ImageHeight",
	0x8769 : "ExifIFDPointer",
	0x8825 : "GPSInfoIFDPointer",
	0xA005 : "InteroperabilityIFDPointer",
	0x0102 : "BitsPerSample",
	0x0103 : "Compression",
	0x0106 : "PhotometricInterpretation",
	0x0112 : "Orientation",
	0x0115 : "SamplesPerPixel",
	0x011C : "PlanarConfiguration",
	0x0212 : "YCbCrSubSampling",
	0x0213 : "YCbCrPositioning",
	0x011A : "XResolution",
	0x011B : "YResolution",
	0x0128 : "ResolutionUnit",
	0x0111 : "StripOffsets",
	0x0116 : "RowsPerStrip",
	0x0117 : "StripByteCounts",
	0x0201 : "JPEGInterchangeFormat",
	0x0202 : "JPEGInterchangeFormatLength",
	0x012D : "TransferFunction",
	0x013E : "WhitePoint",
	0x013F : "PrimaryChromaticities",
	0x0211 : "YCbCrCoefficients",
	0x0214 : "ReferenceBlackWhite",
	0x0132 : "DateTime",
	0x010E : "ImageDescription",
	0x010F : "Make",
	0x0110 : "Model",
	0x0131 : "Software",
	0x013B : "Artist",
	0x8298 : "Copyright"
}

EXIF.GPSTags = {
	0x0000 : "GPSVersionID",
	0x0001 : "GPSLatitudeRef",
	0x0002 : "GPSLatitude",
	0x0003 : "GPSLongitudeRef",
	0x0004 : "GPSLongitude",
	0x0005 : "GPSAltitudeRef",
	0x0006 : "GPSAltitude",
	0x0007 : "GPSTimeStamp",
	0x0008 : "GPSSatellites",
	0x0009 : "GPSStatus",
	0x000A : "GPSMeasureMode",
	0x000B : "GPSDOP",
	0x000C : "GPSSpeedRef",
	0x000D : "GPSSpeed",
	0x000E : "GPSTrackRef",
	0x000F : "GPSTrack",
	0x0010 : "GPSImgDirectionRef",
	0x0011 : "GPSImgDirection",
	0x0012 : "GPSMapDatum",
	0x0013 : "GPSDestLatitudeRef",
	0x0014 : "GPSDestLatitude",
	0x0015 : "GPSDestLongitudeRef",
	0x0016 : "GPSDestLongitude",
	0x0017 : "GPSDestBearingRef",
	0x0018 : "GPSDestBearing",
	0x0019 : "GPSDestDistanceRef",
	0x001A : "GPSDestDistance",
	0x001B : "GPSProcessingMethod",
	0x001C : "GPSAreaInformation",
	0x001D : "GPSDateStamp",
	0x001E : "GPSDifferential"
}

EXIF.StringValues = {
	ExposureProgram : {
		0 : "Not defined",
		1 : "Manual",
		2 : "Normal program",
		3 : "Aperture priority",
		4 : "Shutter priority",
		5 : "Creative program",
		6 : "Action program",
		7 : "Portrait mode",
		8 : "Landscape mode"
	},
	MeteringMode : {
		0 : "Unknown",
		1 : "Average",
		2 : "CenterWeightedAverage",
		3 : "Spot",
		4 : "MultiSpot",
		5 : "Pattern",
		6 : "Partial",
		255 : "Other"
	},
	LightSource : {
		0 : "Unknown",
		1 : "Daylight",
		2 : "Fluorescent",
		3 : "Tungsten (incandescent light)",
		4 : "Flash",
		9 : "Fine weather",
		10 : "Cloudy weather",
		11 : "Shade",
		12 : "Daylight fluorescent (D 5700 - 7100K)",
		13 : "Day white fluorescent (N 4600 - 5400K)",
		14 : "Cool white fluorescent (W 3900 - 4500K)",
		15 : "White fluorescent (WW 3200 - 3700K)",
		17 : "Standard light A",
		18 : "Standard light B",
		19 : "Standard light C",
		20 : "D55",
		21 : "D65",
		22 : "D75",
		23 : "D50",
		24 : "ISO studio tungsten",
		255 : "Other"
	},
	Flash : {
		0x0000 : "Flash did not fire",
		0x0001 : "Flash fired",
		0x0005 : "Strobe return light not detected",
		0x0007 : "Strobe return light detected",
		0x0009 : "Flash fired, compulsory flash mode",
		0x000D : "Flash fired, compulsory flash mode, return light not detected",
		0x000F : "Flash fired, compulsory flash mode, return light detected",
		0x0010 : "Flash did not fire, compulsory flash mode",
		0x0018 : "Flash did not fire, auto mode",
		0x0019 : "Flash fired, auto mode",
		0x001D : "Flash fired, auto mode, return light not detected",
		0x001F : "Flash fired, auto mode, return light detected",
		0x0020 : "No flash function",
		0x0041 : "Flash fired, red-eye reduction mode",
		0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
		0x0047 : "Flash fired, red-eye reduction mode, return light detected",
		0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
		0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
		0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
		0x0059 : "Flash fired, auto mode, red-eye reduction mode",
		0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
		0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
	},
	SensingMethod : {
		1 : "Not defined",
		2 : "One-chip color area sensor",
		3 : "Two-chip color area sensor",
		4 : "Three-chip color area sensor",
		5 : "Color sequential area sensor",
		7 : "Trilinear sensor",
		8 : "Color sequential linear sensor"
	},
	SceneCaptureType : {
		0 : "Standard",
		1 : "Landscape",
		2 : "Portrait",
		3 : "Night scene"
	},
	SceneType : {
		1 : "Directly photographed"
	},
	CustomRendered : {
		0 : "Normal process",
		1 : "Custom process"
	},
	WhiteBalance : {
		0 : "Auto white balance",
		1 : "Manual white balance"
	},
	GainControl : {
		0 : "None",
		1 : "Low gain up",
		2 : "High gain up",
		3 : "Low gain down",
		4 : "High gain down"
	},
	Contrast : {
		0 : "Normal",
		1 : "Soft",
		2 : "Hard"
	},
	Saturation : {
		0 : "Normal",
		1 : "Low saturation",
		2 : "High saturation"
	},
	Sharpness : {
		0 : "Normal",
		1 : "Soft",
		2 : "Hard"
	},
	SubjectDistanceRange : {
		0 : "Unknown",
		1 : "Macro",
		2 : "Close view",
		3 : "Distant view"
	},
	FileSource : {
		3 : "DSC"
	},

	Components : {
		0 : "",
		1 : "Y",
		2 : "Cb",
		3 : "Cr",
		4 : "R",
		5 : "G",
		6 : "B"
	}
}
